#include "SignerV2.h"
#include <algorithm>
#include "util.h"

namespace ks3::sdk {

std::string SignerV2::Sign(const KS3Context& ctx, MethodType method) {
    const std::string strForSign = GetStringForSign(ctx, method);
    return ComputeSignature(strForSign, ctx.secretkey);
}

std::string SignerV2::ComputeSignature(const std::string& str_for_sign, const std::string& secret_key) {
    unsigned char hmac[20] = {'\0'};
    int b64Len = 0;
    char b64[((20 + 1) * 4) / 3+1] = { '\0' };
    HMAC_SHA1(hmac, reinterpret_cast<const unsigned char*>(secret_key.c_str()), static_cast<int>(secret_key.size()),
             reinterpret_cast<const unsigned char*>(str_for_sign.c_str()), static_cast<int>(str_for_sign.size()));
    b64Len = base64Encode(hmac, 20, b64);
    b64[b64Len] = '\0';

    std::string result;
    result.assign(b64);
    return result;
}

std::string SignerV2::GetStringForSign(const KS3Context& ctx, const MethodType method) {
    std::string strForSign;
    std::map<std::string, std::string> canonical_headers;
    for (const auto & it : ctx.headers) {
        std::string header = it.first;
        std::string new_header;
        new_header.assign(header);
        std::transform(new_header.begin(), new_header.end(), new_header.begin(), tolower);
        canonical_headers.insert(std::pair<std::string, std::string>(new_header, it.second));
    }

    // 1. append method
    strForSign.append(MethodToStr(method));
    strForSign.append("\n");

    // 2. append content md5
    auto tit = canonical_headers.find("content-md5");
    if (tit != canonical_headers.end()) {
        strForSign.append(tit->second);
    }
    strForSign.append("\n");

    // 3. append content type
    tit = canonical_headers.find("content-type");
    if (tit != canonical_headers.end()) {
        strForSign.append(tit->second);
    }
    strForSign.append("\n");

    // 4. append date
    tit = canonical_headers.find("date");
    if (tit != canonical_headers.end()) {
        strForSign.append(tit->second);
    }
    strForSign.append("\n");

    // 5. append headers start with x-kss
    tit = canonical_headers.begin();
    for (; tit != canonical_headers.end(); ++tit) {
        if (tit->first.find("x-kss-") == 0) {
            strForSign.append(tit->first);
            strForSign.append(":");
            strForSign.append(tit->second);
            strForSign.append("\n");
        }
    }

    // 6. append canonical resources
    std::string canonicalResources;
    canonicalResources.reserve(1024);
    GetQueryString(ctx, true, &canonicalResources);
    strForSign.append(canonicalResources);
    return strForSign;
}

}
